/****************************************************************
 * Fulguro
 * Copyright (C) 2004 Christophe Clienti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 ***************************************************************/

#include <stdio.h>
#include <string.h>
#include <flgrCoreMalloc.h>
#include <flgrCoreData.h>
#include <flgrCoreVector.h>
#include <flgrCoreIO.h>
#include <flgrCoreDataIO.h>
#include <flgrCoreSlideWindow.h>
#include <flgrCoreNhbManage.h>
#include <flgrCoreCopy.h>
#include <flgrCoreReplace.h>
#include <flgrCoreDispatch.h>
#include <flgrDataToolsPStack.h>

#include "flgrMorphoBase.h"
#include "flgrMorphoWatershed.h"
#include "flgrMorphoWatershedSPP1.h"

/*! \addtogroup group_fulguro_core_morpho_geo
 * @{
 */

#define FLGR_MACRO_COMPARE_PR_LBL(dtype)				\
  dtype *p1 = (dtype*) priorityLabel1->array;				\
  dtype *p2 = (dtype*) priorityLabel2->array;				\
  dtype a1,a2;								\
  int ret;								\
  									\
  a1 = flgr_get_array_##dtype(p1,0);					\
  a2 = flgr_get_array_##dtype(p2,0);					\
  									\
  ret = (a1>a2) ? FLGR_TRUE : FLGR_FALSE;				\
  return ret


int comparePrLbl_fgUINT8(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgUINT8);
}
int comparePrLbl_fgUINT16(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgUINT16);
}
int comparePrLbl_fgUINT32(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgUINT32);
}
int comparePrLbl_fgINT8(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgINT8);
}
int comparePrLbl_fgINT16(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgINT16);
}
int comparePrLbl_fgINT32(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgINT32);
}
int comparePrLbl_fgFLOAT32(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgINT32);
}
int comparePrLbl_fgFLOAT64(FLGR_Vector *priorityLabel1, FLGR_Vector *priorityLabel2) {
  FLGR_MACRO_COMPARE_PR_LBL(fgINT32);
}



/////////////////////////////////////////////////
// Macro for different type of flgr2d_watershed
/////////////////////////////////////////////////
#define WATERSHED_DYNAMIC_STACK(dtype,dtypename,comparefct)		\
  FLGR_Vector *vec;							\
  FLGR_Data2D *nhbOdd,*nhbEven;						\
  FLGR_Data2D *nhb;							\
  FLGR_NhbBox2D *cont;							\
  FLGR_PStack *pstack;							\
  FLGR_McStack *stack;							\
  int *cont_list_x;							\
  int *cont_list_y;							\
  dtype *label_ptr;							\
  dtype *src_ptr;							\
  dtype *cont_ptr;							\
  dtype newcolor;							\
  dtype color;								\
  dtype cont_centerval;							\
  int i,j,w2,h2;							\
  int row, col;								\
  int m,n;								\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  vec = flgr_vector_create(imsrc->spp,imsrc->type);			\
  pstack = flgr_pstack_create(dtypename,comparefct);			\
									\
  /********************************************/			\
  /* Fill Priority Stack with labelized image */			\
  /********************************************/			\
  for(i=0 ; i<imlabel->size_y ; i++) {					\
    label_ptr = (dtype*) imlabel->array[i];				\
    for(j=0 ; j<imlabel->size_x ; j++) {				\
      if(label_ptr[j]>0) {						\
	flgr2d_get_data_vector_##dtype(imsrc,i,j,vec);			\
	flgr_pstack_push_##dtype(pstack,vec, i, j );			\
      }									\
    }									\
  }									\
									\
  /************************/						\
  /* immersion simulation */						\
  /************************/						\
  nhb = flgr2d_create_neighborhood_from_connexity(imsrc->spp,		\
						  dtypename,connexity);	\
  nhbOdd = flgr2d_create_neighborhood_from(nhb);			\
  nhbEven = flgr2d_create_neighborhood_from(nhb);			\
									\
  flgr2d_fill_nhbs_for_6_connexity(nhbEven,nhbOdd,nhb,FLGR_NHB_NO_SYM);	\
									\
  flgr2d_destroy(nhb);							\
									\
  cont = flgr2d_create_neighbor_box(nhbOdd);				\
  w2 = cont->nhb_size_x/2;						\
  h2 = cont->nhb_size_y/2;						\
									\
  cont_list_x = cont->list_coord_x[0];					\
  cont_list_y = cont->list_coord_y[0];					\
									\
  stack = flgr_pstack_get_first_no_empty_stack(pstack);			\
  i=0;									\
  while( i < pstack->nbPriority) {					\
									\
    color = *((dtype *) stack->priorityLabel->array);			\
									\
    while(stack->size>0) {						\
      flgr_pstack_pop_stack(stack, &row, &col);				\
									\
      nhb = (((row%2)==1) ? nhbOdd : nhbEven);				\
									\
      flgr2d_get_neighborhood(cont,imlabel,nhb,row,col);		\
									\
      cont_ptr = (dtype*) cont->list_data_val[0];			\
      cont_centerval = *((dtype*) cont->center_data_val->array);	\
									\
      for(j=0 ; j<cont->size[0] ; j++) {				\
	m=row+cont_list_y[j]-h2;					\
	n=col+cont_list_x[j]-w2;					\
	if(cont_ptr[j]==0) {						\
	  label_ptr = imlabel->array[m];				\
	  label_ptr[n] = cont_centerval;				\
									\
	  src_ptr = imsrc->array[m];					\
	  newcolor = src_ptr[n];					\
									\
	  if(newcolor<color) {						\
	    if(stack->next==NULL) {					\
	      newcolor = color;						\
	    } else {							\
	      newcolor = *((dtype *) stack->next->priorityLabel->array); \
	    }								\
	  }/*if the stack priority have already been computed*/		\
	  flgr_vector_populate_from_scalar_##dtype(vec,newcolor);	\
	  flgr_pstack_push_##dtype(pstack,vec,m,n);			\
									\
	}/*if one neighbor is not labelized*/				\
      }/*for every neighbor*/						\
									\
    }/*while current stack is not empty*/				\
    stack = stack->next;						\
    i=i+1;								\
  }/*while all stack are not processed*/				\
									\
  flgr_pstack_destroy(pstack);						\
  flgr_vector_destroy(vec);						\
  return

void flgr2d_watershed_fgBIT(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_BIT);
}

void flgr2d_watershed_fgUINT8(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  if(imsrc->spp==1) {
    flgr2d_watershed_spp1_fgUINT8(imlabel, imsrc, connexity);
  }else {
    WATERSHED_DYNAMIC_STACK(fgUINT8, FLGR_UINT8, comparePrLbl_fgUINT8);
  }
}

void flgr2d_watershed_fgUINT16(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  if(imsrc->spp==1) {
    flgr2d_watershed_spp1_fgUINT16(imlabel, imsrc, connexity);
  }else {
    WATERSHED_DYNAMIC_STACK(fgUINT16, FLGR_UINT16, comparePrLbl_fgUINT16);
  }
}

void flgr2d_watershed_fgUINT32(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  WATERSHED_DYNAMIC_STACK(fgUINT32,FLGR_UINT32, comparePrLbl_fgUINT32);
}

void flgr2d_watershed_fgINT8(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  WATERSHED_DYNAMIC_STACK(fgINT8,FLGR_INT8, comparePrLbl_fgINT8);
}

void flgr2d_watershed_fgINT16(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  WATERSHED_DYNAMIC_STACK(fgINT16,FLGR_INT16, comparePrLbl_fgINT16);
}

void flgr2d_watershed_fgINT32(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  WATERSHED_DYNAMIC_STACK(fgINT32,FLGR_INT32, comparePrLbl_fgINT32);
}

void flgr2d_watershed_fgFLOAT32(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  WATERSHED_DYNAMIC_STACK(fgFLOAT32,FLGR_FLOAT32, comparePrLbl_fgFLOAT32);
}

void flgr2d_watershed_fgFLOAT64(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  WATERSHED_DYNAMIC_STACK(fgFLOAT64,FLGR_FLOAT64, comparePrLbl_fgFLOAT64);
}

////////////////////////////////////////////////////////////////////////////
/*! Standard Watershed by immersion (this version is biased)
 *  @param imlabel : labelized image
 *  @param imsrc : image source
 *  @param connexity : connexity (4-connexity,6-connexity,8-connexity)
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_watershed(FLGR_Data2D *imlabel, FLGR_Data2D *imsrc, FLGR_Connexity connexity) {
  FLGR_Ret ret;

  FLGR_DECLARE_FUNCTION;

  if((imlabel==NULL) || (imsrc==NULL)){
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(imlabel->spp!=1)  {
    POST_ERROR("Multi Sample Per Pixel not supported\n");
    return FLGR_RET_UNDEFINED_ERROR;
  }

  if((ret=flgr2d_is_data_same_attributes(imlabel,imsrc,__FUNCTION__))!=FLGR_RET_OK) return ret;

  FLGR_DISPATCH_PROCEDURE(imlabel->type,flgr2d_watershed,imlabel,imsrc,connexity);

}









#define FLGR_MACRO_WSHED_BUILD_LINE(dtype)			\
  FLGR_Data2D *nhb;						\
  FLGR_DECLARE_FUNCTION;					\
  nhb = flgr2d_create_neighborhood_from_connexity(imdest->spp,	\
						  imdest->type,	\
						  connexity);	\
  flgr2d_gradient_outer(imdest,imlabel,nhb);			\
  flgr2d_replace_I_with_S_S_S(imdest,imdest,"!=","0","1","0");	\
  flgr2d_destroy(nhb)




void flgr2d_watershed_build_line_fgBIT(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_BIT);
}
void flgr2d_watershed_build_line_fgUINT8(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgUINT8);
}
void flgr2d_watershed_build_line_fgUINT16(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgUINT16);
}
void flgr2d_watershed_build_line_fgUINT32(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgUINT32);
}
void flgr2d_watershed_build_line_fgINT8(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgINT8);
}
void flgr2d_watershed_build_line_fgINT16(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgINT16);
}
void flgr2d_watershed_build_line_fgINT32(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgINT32);
}
void flgr2d_watershed_build_line_fgFLOAT32(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgFLOAT32);
}
void flgr2d_watershed_build_line_fgFLOAT64(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_MACRO_WSHED_BUILD_LINE(fgFLOAT64);
}

////////////////////////////////////////////////////////////////////////////
/*! Construct watershed line
 *  @param imdest : destination
 *  @param imlabel : watersheded label image
 *  @param connexity : connexity (4-connexity,6-connexity,8-connexity)
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_watershed_build_line(FLGR_Data2D *imdest, FLGR_Data2D *imlabel, FLGR_Connexity connexity) {
  FLGR_Ret ret;

  FLGR_DECLARE_FUNCTION;

  if((imdest==NULL) || (imlabel==NULL)) {
    POST_ERROR("Null object\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if((ret=flgr2d_is_data_same_attributes(imlabel,imdest,__FUNCTION__))!=FLGR_RET_OK) return ret;

  FLGR_DISPATCH_PROCEDURE(imlabel->type,flgr2d_watershed_build_line,imdest,imlabel,connexity);



}


//!@}
